package org.cedar.syntaxcore;

/**
 * <p>Title: 将表达式转换为后缀式</p>
 * <p>Description:
 * 将表达式转化为 能被运算器单元Calculator使用的Nipolan式
 *  </p>
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Company: net.wzh</p>
 * @author net.wzh
 * @version 1.0
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;

public class Parser {
	  private int pos;
	  private static ThreadLocal<HashMap<String, Queue>> localParserMap = new ThreadLocal<HashMap<String,Queue>>();
	  private HashMap<String, Queue> parserResult;
	  public Parser() {
		  if(localParserMap.get() == null){
			  localParserMap.set( new HashMap<String, Queue>());
		  }
		  parserResult = localParserMap.get();
	  }
	  
	  private void putIntoParserCache(String parserStr, Queue parserResult){
		  this.parserResult.put(parserStr, parserResult);
	  }
	  private Queue getParserResultFromCache(String parserStr){
		  Queue ret = null;
		  if(null != this.parserResult)
			  ret = this.parserResult.get(parserStr);
		  if(null != ret)
			  ret.resetPos();
		  return ret;
	  }
	  private Stack scOp;
	  public Queue eqResult;
	  private static char[] strDividers = {
	      '+', '-', '*', '/', '(', ')', '<', '>', '\n', '\t', ' ', '!', '&', '|'};

	  public void parseIt(String strForParsing) {
	    String strSrc = strForParsing.trim();
		eqResult = getParserResultFromCache(strSrc);
		if(null != eqResult)
			return;
	    scOp = new Stack();
	    eqResult = new Queue();
	    pos = 0;
	    boolean isFirstOperator = true;
	    int opCurrent;
	    int sL = strSrc.length();
	    while (pos < sL) {
	      if ( (opCurrent = isOperator(strSrc, isFirstOperator)) !=
	          EnmOperators.Nop) {
	        if (opCurrent != EnmOperators.Blank) {
	          operatorStackManager(opCurrent);
	          if (opCurrent == EnmOperators.RightPar)
	            isFirstOperator = false;
	          else
	            isFirstOperator = true;
	        }
	        continue;
	      }
	      if(pos >= sL)
	    	  break;
	      eqResult.enQueue(getOperand(strSrc));
	      isFirstOperator = false;
	    }
	    while (scOp.size() > 0) {
	      eqResult.enQueue(new ExecutionItem(((Integer)scOp.pop()).intValue()));
	    }
	    scOp.clear();
	    this.putIntoParserCache(strSrc, eqResult);
	  }

  private int isOperator(String s, boolean isFirstOperator) {
    int curOperator = EnmOperators.Nop;
    switch (s.charAt(pos)) {
      case ' ':
      case '\n':
      case '\t':
        pos++;
        curOperator = EnmOperators.Blank;
        break;
      case '+':
        if (isFirstOperator)
          curOperator = EnmOperators.UnPlus;
        else
          curOperator = EnmOperators.Plus;
        pos++;
        break;
      case '-':
        if (isFirstOperator)
          curOperator = EnmOperators.UnMinus;
        else
          curOperator = EnmOperators.Minus;
        pos++;
        break;
      case '*':
        curOperator = EnmOperators.Mul;
        pos++;
        break;
      case '/':
    	if(scanNotes(s) > -1 && pos < s.length())
    		curOperator = isOperator(s,isFirstOperator);
    	if(pos >= s.length())
    		break;
    	if(curOperator == EnmOperators.Nop && '/' == s.charAt(pos)){
	        curOperator = EnmOperators.Div;
	        pos++;
    	}
        break;
      case '%':
    	  curOperator = EnmOperators.Mod;
    	  pos++;
    	  break;
      case '=':
        if ( (s.length() > (pos + 1)) && (s.charAt(pos + 1) == '=')) {
          curOperator = EnmOperators.Eq;
          pos += 2;
        }else{
            curOperator = EnmOperators.Assign;
            pos++;
        }
        break;
      case '(':
        curOperator = EnmOperators.LeftPar;
        pos++;
        break;
      case ')':
        curOperator = EnmOperators.RightPar;
        pos++;
        break;
      case '&':
        curOperator = EnmOperators.And;
        pos++;
        break;
      case '|':
        curOperator = EnmOperators.Or;
        pos++;
        break;
      case '!':
        if ( (s.length() > (pos + 1)) && (s.charAt(pos) == '=')) {
          curOperator = EnmOperators.NtEq;
          pos += 2;
        }
        else {
          curOperator = EnmOperators.Not;
          pos++;
        }
        ;
        break;
      case '>':
        if ( (s.length() > (pos + 1)) && (s.charAt(pos + 1) == '=')) {
          curOperator = EnmOperators.GrEq;
          pos += 2;
        }
        else {
          curOperator = EnmOperators.Gr;
          pos++;
        }
        break;
      case '<':
        if ( (s.length() > (pos + 1)) && (s.charAt(pos + 1) == '=')) {
          curOperator = EnmOperators.LsEq;
          pos += 2;
        }
        else
        if ( (s.length() > (pos + 1)) && (s.charAt(pos + 1) == '>')) {
          curOperator = EnmOperators.NtEq;
          pos += 2;
        }
        else {
          curOperator = EnmOperators.Ls;
          pos++;
        }
        break;
    }
    return curOperator;
  }

  private int getPrivelege(int op) {
    switch (op) {
      case EnmOperators.LeftPar:
        return 0;
      case EnmOperators.RightPar:
      case EnmOperators.Not:
      case EnmOperators.Or:
      case EnmOperators.And:
      case EnmOperators.Assign:
        return 1;
      case EnmOperators.Eq:
      case EnmOperators.NtEq:
      case EnmOperators.GrEq:
      case EnmOperators.LsEq:
      case EnmOperators.Gr:
      case EnmOperators.Ls:
        return 2;
      case EnmOperators.Plus:
      case EnmOperators.Minus:

        //case EnmOperators.Or:
        return 3;
      case EnmOperators.Mul:
      case EnmOperators.Div:
      case EnmOperators.Mod:
        return 4;
      case EnmOperators.UnMinus:
      case EnmOperators.UnPlus:
        return 5;
      default:
        return 6;
    }
  }

  private void operatorStackManager(int op) {
    if (scOp.size() == 0) {
      scOp.push(new Integer(op));
      return;
    }
    if (op == EnmOperators.LeftPar) {
      scOp.push(new Integer(op));
      return;
    }
    if (op == EnmOperators.RightPar) {
      while (scOp.size() > 0) {
        int opStack = ((Integer)scOp.pop()).intValue();
        if (opStack != EnmOperators.LeftPar)
          eqResult.enQueue(new ExecutionItem(opStack));
        else
          return;
      }
      return;
    }
    while (scOp.size() > 0) {
      int opStack = ((Integer)scOp.peek()).intValue();
      if (getPrivelege(op) <= getPrivelege(opStack))
        eqResult.enQueue(new ExecutionItem(((Integer)scOp.pop()).intValue()));
      else
        break;
    }
    scOp.push(new Integer(op));
  }

  private ExecutionItem getOperand(String strSrc) {
    char c = strSrc.charAt(pos);
    if (c == '\"') {
      return new ExecutionItem(ItemType.itString, getString(strSrc));
    }
    if (c == '\'') {
      return new ExecutionItem(ItemType.itString, getString2(strSrc));
    }
    if (c == '[') {
      return new ExecutionItem(ItemType.itVariable,
                               getVariable(strSrc));
    }
    if (c == '$') {
    	return new ExecutionItem(ItemType.itVariable,
    			getVariable(strSrc));
    }
    if (charIsDigit(c) || (c == '.')) {
      return new ExecutionItem(ItemType.itDigit, getNumber(strSrc));
    }
    if (c == '#') {
      return new ExecutionItem(ItemType.itDate, getDate(strSrc));
    }

    int i = indexOfAny(strSrc,strDividers, pos);
    if (i < 0)
      i = strSrc.length();
    String s = strSrc.substring(pos,i);
    pos = i;
    if ( (pos < strSrc.length()) && (strSrc.charAt(pos) == '(')) {
      return new ExecutionItem(s, getFunction(strSrc));
    }
    if ("true".equalsIgnoreCase(s))
      return new ExecutionItem(true);
    if ("false".equalsIgnoreCase(s))
      return new ExecutionItem(false);

    return new ExecutionItem(ItemType.itString, s);
  }

  private String getString(String strSrc) {
    int oldPos = pos;
    pos++;
    while ( (pos < strSrc.length()) && (strSrc.charAt(pos) != '\"')) {
      pos++;
    }
    pos++;
    return strSrc.substring(oldPos + 1, pos  - 1);
  }

  private String getString2(String strSrc) {
    int oldPos = pos;
    pos++;
    while ( (pos < strSrc.length()) && (strSrc.charAt(pos) != '\'')) {
      pos++;
    };
    pos++;
    return strSrc.substring(oldPos + 1, pos  - 1);
  }

  private String getDate(String strSrc) {
    int oldPos = pos;
    pos++;
    while ( (pos < strSrc.length()) && (strSrc.charAt(pos) != '#')) {
      pos++;
    }
    ;
    pos++;
    return strSrc.substring(oldPos + 1,  pos - 1);
  }

  private String getVariable(String strSrc) {
    int lrBr = 1;
    if(strSrc.charAt(pos) == '$')
    	pos++;
    int oldPos = pos;
    pos++;
    while ( (pos < strSrc.length())) {
      switch (strSrc.charAt(pos)) {
        case '[':
          lrBr++;
          break;
        case '{':
        	lrBr++;
        	break;
        case ']':
          lrBr--;
          break;
        case '}':
        	lrBr--;
        	break;
      }
      pos++;
      if (lrBr == 0)
        break;
    }
    return strSrc.substring(oldPos + 1,  pos  - 1);
  }

  private String getNumber(String strSrc) {
    int oldPos = pos;
    char c = ' ';
    while ( (pos < strSrc.length())){
    	c = strSrc.charAt(pos);
    	if (charIsDigit(c) || c == '.' || c == 'D' || c == 'L' || c == 'd' || c == 'l')
    		pos++;
    	else
    		break;
    }
    return strSrc.substring(oldPos,  pos );
  }

  private ArrayList getFunction(String strSrc) {
    int oldPos = ++pos;
    boolean noStr = true;
    int parentLevel = 1;
    int parentLevel_ = 1;//中括号
    ArrayList scTemp = new ArrayList();

    while (! ( (strSrc.charAt(pos) == ')') && (parentLevel == 1) && noStr) &&
           (strSrc.length() > pos)) {
      switch (strSrc.charAt(pos)) {
        case '\"':
          noStr = !noStr;
          break;
        case '[':
        	parentLevel_++;
        	break;
        case ']':
        	parentLevel_--;
        	break;
        case '(':
          if (noStr)
            parentLevel++;
          break;
        case ')':
          if (noStr)
            parentLevel--;
          break;
        case ',':
          if ( (parentLevel == 1) && (parentLevel_ == 1) && noStr) {
            scTemp.add(strSrc.substring(oldPos,  pos ));
            oldPos = pos + 1;
            break;
          }
          break;
      }
      pos++;
    }
    scTemp.add(strSrc.substring(oldPos,  pos ));
    pos++;
    return scTemp;
  }
  private boolean charIsDigit(char c){
    return c>='0' && c<= '9';
  }
  private int indexOfAny(String src,char[] cArr,int pos){
    for(int i=pos;i<src.length();i++){
      char t = src.charAt(i);
      for(int j =0;j<cArr.length;j++){
        if(t == cArr[j])
          return i;
      }
    }
    return -1;
  }
  private int scanNotes(String src){
	int len =  src.length();
	if(src.charAt(pos) == '/' && pos+ 1 < len &&  src.charAt(pos+1) == '*'){
		pos+=2;
		
		while(pos < len - 1){
			if( '*' == src.charAt(pos) && '/' == src.charAt(pos+1) ){
				pos+=2;
				return pos;
			}
			pos++;
		}
		throw new RuntimeException("语法错误： 代码注释没有闭合!");
	}
	return -1;
  }
}
